using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace GameFrame
{
    [StructLayout(LayoutKind.Explicit)]
    public struct ComponentSet
    {
        public const int SIZE = 32;
        public const int BLOCK_COUNT = 4;
        public const int BITS_PER_BLOCK = 64;
        public const int MAX_COMPONENTS = 256;
        [FieldOffset(0)]
        public unsafe fixed ulong _set[4];
       
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe bool IsEmpty => this._set[0] == 0UL && this._set[1] == 0UL && this._set[2] == 0UL && this._set[3] == 0UL;
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Add<T>() where T : unmanaged, IComponent => this.Add(ComponentTypeId<T>.Id);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Remove<T>() where T : unmanaged, IComponent => this.Remove(ComponentTypeId<T>.Id);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsSet<T>() where T : unmanaged, IComponent => this.IsSet(ComponentTypeId<T>.Id);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe void Add(int index) => this._set[index / 64] |= (ulong) (1L << index % 64);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe void Remove(int index) => this._set[index / 64] &= (ulong) ~(1L << index % 64);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public unsafe bool IsSet(int index) => (this._set[index / 64] & (ulong) (1L << index % 64)) > 0UL;

        public unsafe bool Equals(ComponentSet other) => (long) this._set.FixedElementField == (long) other._set.FixedElementField && (long) this._set[1] == (long) other._set[1] && (long) this._set[2] == (long) other._set[2] && (long) this._set[3] == (long) other._set[3];

        public override bool Equals(object obj) => obj is ComponentSet other && this.Equals(other);

        public override unsafe int GetHashCode() => HashCodeUtils.CombineHashCodes(HashCodeUtils.CombineHashCodes(HashCodeUtils.CombineHashCodes(HashCodeUtils.UInt64HashCode(this._set.FixedElementField), HashCodeUtils.UInt64HashCode(this._set[1])), HashCodeUtils.UInt64HashCode(this._set[2])), HashCodeUtils.UInt64HashCode(this._set[3]));

        public override string ToString()
        {
            List<string> stringList = new List<string>();
            for (int index = 1; index < ComponentTypeId.Type.Length; ++index)
            {
                if (this.IsSet(index))
                    stringList.Add(ComponentTypeId.Type[index].Name);
            }
            return "[ComponentSet: (" + string.Join(", ", stringList.ToArray()) + ")]";
        }

        public unsafe bool Equals(in ComponentSet other) => (long) this._set.FixedElementField == (long) other._set.FixedElementField && (long) this._set[1] == (long) other._set[1] && (long) this._set[2] == (long) other._set[2] && (long) this._set[3] == (long) other._set[3];

        public unsafe bool IsSupersetOf(in ComponentSet other) => ((long) this._set.FixedElementField & (long) other._set.FixedElementField) == (long) other._set.FixedElementField && ((long) this._set[1] & (long) other._set[1]) == (long) other._set[1] && ((long) this._set[2] & (long) other._set[2]) == (long) other._set[2] && ((long) this._set[3] & (long) other._set[3]) == (long) other._set[3];

        public unsafe bool IsSubsetOf(in ComponentSet other) => ((long) this._set.FixedElementField & (long) other._set.FixedElementField) != (long) this._set.FixedElementField && ((long) this._set[1] & (long) other._set[1]) != (long) this._set[1] && ((long) this._set[2] & (long) other._set[2]) != (long) this._set[2] && ((long) this._set[3] & (long) other._set[3]) != (long) this._set[3];

        public unsafe bool Overlaps(in ComponentSet other) => ((long) this._set.FixedElementField & (long) other._set.FixedElementField) != 0L || ((long) this._set[1] & (long) other._set[1]) != 0L || ((long) this._set[2] & (long) other._set[2]) != 0L || (this._set[3] & other._set[3]) > 0UL;

        public unsafe void UnionWith(in ComponentSet other)
        {
            // ISSUE: reference to a compiler-generated field
            // ISSUE: reference to a compiler-generated field
            this._set.FixedElementField |= other._set.FixedElementField;
            this._set[1] |= other._set[1];
            this._set[2] |= other._set[2];
            this._set[3] |= other._set[3];
        }

        public unsafe void IntersectWith(in ComponentSet other)
        {
            // ISSUE: reference to a compiler-generated field
            // ISSUE: reference to a compiler-generated field
            this._set.FixedElementField &= other._set.FixedElementField;
            this._set[1] &= other._set[1];
            this._set[2] &= other._set[2];
            this._set[3] &= other._set[3];
        }

        public unsafe void Remove(in ComponentSet other)
        {
            // ISSUE: reference to a compiler-generated field
            // ISSUE: reference to a compiler-generated field
            this._set.FixedElementField &= ~other._set.FixedElementField;
            this._set[1] &= ~other._set[1];
            this._set[2] &= ~other._set[2];
            this._set[3] &= ~other._set[3];
        }

        public static unsafe void Serialize(void* ptr, FrameSerializer serializer)
        {
            ComponentSet* componentSetPtr = (ComponentSet*) ptr;
            for (int index = 0; index < 4; ++index)
                serializer.Stream.Serialize(componentSetPtr->_set + index);
        }

        public static ComponentSet Create<T0>() where T0 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            return componentSet;
        }

        public static ComponentSet Create<T0, T1>()
            where T0 : unmanaged, IComponent
            where T1 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            componentSet.Add<T1>();
            return componentSet;
        }

        public static ComponentSet Create<T0, T1, T2>()
            where T0 : unmanaged, IComponent
            where T1 : unmanaged, IComponent
            where T2 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            componentSet.Add<T1>();
            componentSet.Add<T2>();
            return componentSet;
        }

        public static ComponentSet Create<T0, T1, T2, T3>()
            where T0 : unmanaged, IComponent
            where T1 : unmanaged, IComponent
            where T2 : unmanaged, IComponent
            where T3 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            componentSet.Add<T1>();
            componentSet.Add<T2>();
            componentSet.Add<T3>();
            return componentSet;
        }

        public static ComponentSet Create<T0, T1, T2, T3, T4>()
            where T0 : unmanaged, IComponent
            where T1 : unmanaged, IComponent
            where T2 : unmanaged, IComponent
            where T3 : unmanaged, IComponent
            where T4 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            componentSet.Add<T1>();
            componentSet.Add<T2>();
            componentSet.Add<T3>();
            componentSet.Add<T4>();
            return componentSet;
        }

        public static ComponentSet Create<T0, T1, T2, T3, T4, T5>()
            where T0 : unmanaged, IComponent
            where T1 : unmanaged, IComponent
            where T2 : unmanaged, IComponent
            where T3 : unmanaged, IComponent
            where T4 : unmanaged, IComponent
            where T5 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            componentSet.Add<T1>();
            componentSet.Add<T2>();
            componentSet.Add<T3>();
            componentSet.Add<T4>();
            componentSet.Add<T5>();
            return componentSet;
        }

        public static ComponentSet Create<T0, T1, T2, T3, T4, T5, T6>()
            where T0 : unmanaged, IComponent
            where T1 : unmanaged, IComponent
            where T2 : unmanaged, IComponent
            where T3 : unmanaged, IComponent
            where T4 : unmanaged, IComponent
            where T5 : unmanaged, IComponent
            where T6 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            componentSet.Add<T1>();
            componentSet.Add<T2>();
            componentSet.Add<T3>();
            componentSet.Add<T4>();
            componentSet.Add<T5>();
            componentSet.Add<T6>();
            return componentSet;
        }

        public static ComponentSet Create<T0, T1, T2, T3, T4, T5, T6, T7>()
            where T0 : unmanaged, IComponent
            where T1 : unmanaged, IComponent
            where T2 : unmanaged, IComponent
            where T3 : unmanaged, IComponent
            where T4 : unmanaged, IComponent
            where T5 : unmanaged, IComponent
            where T6 : unmanaged, IComponent
            where T7 : unmanaged, IComponent
        {
            ComponentSet componentSet = new ComponentSet();
            componentSet.Add<T0>();
            componentSet.Add<T1>();
            componentSet.Add<T2>();
            componentSet.Add<T3>();
            componentSet.Add<T4>();
            componentSet.Add<T5>();
            componentSet.Add<T6>();
            componentSet.Add<T7>();
            return componentSet;
        }
    }
}